﻿using System;
using System.Collections.Generic;
using Diagnostics.Annotations;
using MobilePal.Collections;
using MobilePal.Log;
using MobilePal.State;

namespace MobilePal.ProcessorAgents
{
    public sealed class CategoriesAgent:IProcessorAgent
    {
        public void AttachToState(List<ILogger> loggers , [NotNull] ApplicationState applicationState)
        {
            Categories = applicationState.Categories;
            Activities = applicationState.Activities;

            applicationState.Activities.CollectionChanged += ActivitiesCollectionChanged;
            applicationState.Categories.CollectionChanged += CategoriesCollectionChanged;
        }

        public void DetachFromState([NotNull] ApplicationState applicationState)
        {
            applicationState.Activities.CollectionChanged -= ActivitiesCollectionChanged;
            applicationState.Categories.CollectionChanged -= CategoriesCollectionChanged;
        }

        private ActivityCategoryCollection Categories
        {
            get;
            set;
        }

        private ActivityCollection Activities
        {
            get;
            set;
        }

        #region Methods

        private void ActivitiesCollectionChanged(object sender, NotifyCollectionChangedEventArgs<Activity> e)
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    OnActivitiesAdded(e.NewItems);
                    break;
                case NotifyCollectionChangedAction.Remove:
                    OnActivitiesRemoved(e.OldItems);
                    break;
                case NotifyCollectionChangedAction.Replace:
                    OnActivitiesRemoved(e.OldItems);
                    OnActivitiesAdded(e.NewItems);
                    break;
            }
        }

        private void OnActivitiesAdded(IEnumerable<Activity> activities)
        {
            foreach (Activity activity in activities)
            {
                foreach (ActivityCategory category in activity.Categories)
                {
                    if (!Categories.Contains(category))
                    {
                        Categories.Add(category);
                    }
                }
            }
        }

        private void OnActivitiesRemoved(IEnumerable<Activity> oldItems)
        {
        }

        private void CategoriesCollectionChanged(object sender, NotifyCollectionChangedEventArgs<ActivityCategory> e)
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    OnCategoriesAdded(e.NewItems);
                    break;

                case NotifyCollectionChangedAction.Remove:
                    OnCategoriesRemoved(e.OldItems);
                    break;

                case NotifyCollectionChangedAction.Replace:
                    OnCategoriesRemoved(e.OldItems);
                    OnCategoriesAdded(e.NewItems);
                    break;
            }
        }

        private void OnCategoriesAdded(IEnumerable<ActivityCategory> newItems)
        {

        }

        /// <exception cref="InvalidOperationException">
        /// Cannot remove the default category.
        /// </exception>
        private void OnCategoriesRemoved(ICollection<ActivityCategory> categories)
        {
            if (categories.Contains(Categories.DefaultCategory))
            {
                throw new InvalidOperationException("Cannot remove the default category.");
            }

            foreach (ActivityCategory category in categories)
            {
                RemoveCategoryFromActivities(category, Activities);
            }
        }

        private static void RemoveCategoryFromActivities(ActivityCategory category, IEnumerable<Activity> activities)
        {
            foreach (Activity activity in activities)
            {
                if (activity.Categories.Contains(category))
                {
                    activity.Categories.Remove(category);
                }
            }
        }

        #endregion
    }
}
